﻿using System.Diagnostics;
using System.Drawing;
using OperationPlugins.ExtensionMethods;
using PerpetuumSoft.Knockout;
using System;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using TimeProject.WebSite.ViewModels;

namespace TimeProject.WebSite.Views
{
    public static class HtmlExtensions
    {
        private const string controllerName = "TimeProject";

        public static HtmlString GetCopyright(this HtmlHelper html)
        {
            return new HtmlString("Time:Project v1.0.0 Copyright © 2014 Ossi Vartiainen");
        }

        public static HtmlString GetLicence(this HtmlHelper html)
        {
            string licence;
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TimeProject.WebSite.Licence.txt"))
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    licence = reader.ReadToEnd();
                }
            }

            return new HtmlString(licence);
        }

        public static MvcHtmlString GetInfoBox(this HtmlHelper html, MessageContainer messages)
        {
            return html.Partial("_infoboxlauncher", messages);
        }

        public static KnockoutBinding<TModel> GetSpan<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.Span(text, new { @type = "text" });
        }

        public static MvcHtmlString GetHelpText<TModel>(this HtmlHelper<TModel> html, string text)
        {
            return MvcHtmlString.Create("<span class=\"help-text\"><i class=\"fa fa-quote-left help-text-logo\"></i> " + HttpUtility.HtmlEncode(text) + " <i class=\"fa fa-quote-right help-text-logo\"></i></span>");
        }

        public static MvcHtmlString GetInfoText<TModel>(this HtmlHelper<TModel> html, string text)
        {
            return MvcHtmlString.Create("<span class=\"info-text\">" + HttpUtility.HtmlEncode(text) + "</span>");
        }

        public static MvcHtmlString GetSubHeading<TModel>(this HtmlHelper<TModel> html, string text)
        {
            return MvcHtmlString.Create("<div class=\"subheading-text\">" + HttpUtility.HtmlEncode(text) + "</div>");
        }

        public static MvcHtmlString GetLabel<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
        {
            return html.LabelFor(expression);
        }

        public static MvcHtmlString GetLabel<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> expression)
        {
            MemberExpression body = expression.Body as MemberExpression;            
            if (body == null)
            {
                throw new InvalidOperationException("The expression parameter is not an instance of the MemberExpression class.");
            }

            string propertyName = body.Member.Name;

            Type vm = body.Member.DeclaringType;

            PropertyInfo property = vm.GetPublicProperty(propertyName);
            if (property == null)
            {
                throw new InvalidOperationException("Could not find an underlying PropertyInfo object.");
            }

            string label;

            DisplayAttribute display = property.GetAttribute<DisplayAttribute>();
            if (display != null)
            {
                label = display.Name;
            }
            else
            {
                label = propertyName;
            }

            return new MvcHtmlString(label);
        }


        public static KnockoutBinding<TModel> GetLoginButton<TModel>(this KnockoutHtml<TModel> html, string text, string actionName)
        {
            return html.Button(text, actionName, controllerName, null, new { @class = "btn btn-inverse btn-login" });
        }

        public static KnockoutBinding<TModel> GetButton<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName = null)
        {
            var binding = html.Button(text, actionName, controllerName, null, new { @class = "btn-command" });
            if (imageName != null)
            {
                binding.InnerHtml = string.Format("<i class=\"{0}\"></i> {1}", imageName, text);
            }
            return binding;
        }

        public static KnockoutBinding<TModel> GetButtonRight<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName = null)
        {
            var binding = html.Button(text, actionName, controllerName, null, new { @class = "btn-command pull-right" });
            if (imageName != null)
            {
                binding.InnerHtml = string.Format("<i class=\"{0}\"></i> {1}", imageName, text);
            }
            return binding;
        }

        public static KnockoutTagBuilder<TModel> GetTdImageLink<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName, object routeValues = null)
        {
            // GetButtonRight
            var binding = html.Button(text, actionName, controllerName, routeValues, new { @class = "app-td-image-link" });

//            var binding = html.HyperlinkButton(text, actionName, "TimeProject", routeValues, new { @class = "app-td-image-link", @style = "padding-left: 15px; padding-right: 15px;" });
            binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i>{2}", imageName, text, "");
            return binding;
        }
        

        //public static KnockoutBinding<TModel> GetHyperlinkButton<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName)
        //{
        //    //if (imageName != null)
        //    //{
        //        var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link", @style = "padding-left: 15px; padding-right: 15px;" });
        //        binding.InnerHtml = string.Format("<i class=\"{0}\"></i> {1}", imageName, "");
        //        return binding;
        //    //}
        //    //else
        //    //{
        //    //    var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link", @style = "padding-left: 15px; padding-right: 15px;" });
        //    //    return binding;
        //    //}
        //}

        public static KnockoutBinding<TModel> GetImageLinkRight<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName)
        {
            // GetButtonRight
            var binding = html.Button(text, actionName, controllerName, null, new { @class = "app-image-link pull-right" });

            //            var binding = html.HyperlinkButton(text, actionName, "TimeProject", routeValues, new { @class = "app-td-image-link", @style = "padding-left: 15px; padding-right: 15px;" });
            binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i>{2}", imageName, text, "");
            return binding;

            ////if (imageName != null)
            ////{
            //var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link pull-right", @style = "padding-left: 15px; padding-right: 15px;" });
            //    binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i> <span style=\"font-size: 12px;\">{2}</span>", imageName, text, "");
            //    return binding;                
            ////}
            ////else
            ////{
            ////    var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link pull-right", @style = "padding-left: 15px; padding-right: 15px;" });
            ////    return binding;
            ////}
        }

        public static KnockoutBinding<TModel> GetImageLink<TModel>(this KnockoutHtml<TModel> html, string text, string actionName, string imageName)
        {
            var binding = html.Button(text, actionName, controllerName, null, new { @class = "app-image-link" });

            //            var binding = html.HyperlinkButton(text, actionName, "TimeProject", routeValues, new { @class = "app-td-image-link", @style = "padding-left: 15px; padding-right: 15px;" });
            binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i>{2}", imageName, text, "");
            return binding;

            ////if (imageName != null)
            ////{
            //var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link pull-right", @style = "padding-left: 15px; padding-right: 15px;" });
            //    binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i> <span style=\"font-size: 12px;\">{2}</span>", imageName, text, "");
            //    return binding;                
            ////}
            ////else
            ////{
            ////    var binding = html.HyperlinkButton(text, actionName, "TimeProject", null, new { @class = "app-action-link pull-right", @style = "padding-left: 15px; padding-right: 15px;" });
            ////    return binding;
            ////}
        }

        //////public static MvcHtmlString GetActionLink<TModel>(this HtmlHelper<TModel> html, string linkText, string actionName)
        //////{
        //////    string s;
        //////    //if (imageName != null)
        //////    //{
        //////        //s = string.Format(
        //////        //        "<a class=\"app-link\" href=\"/{0}?Length=11\" style=\"padding-left: 15px; padding-right: 15px; \"><i class=\"{1}\"></i> {2}</a>",
        //////        //        actionName,
        //////        //        imageName,
        //////        //        linkText);

        //////    //}
        //////    //else
        //////    //{
        //////    s = string.Format(
        //////            "<a class=\"app-link\" href=\"/{0}?Length=11\" style=\"padding-left: 15px; padding-right: 15px; \">{1}</a>",
        //////            actionName,
        //////            linkText);
        //////    //}
        //////    return MvcHtmlString.Create(s);
        //////}

        public static MvcHtmlString GetLinkRight<TModel>(this HtmlHelper<TModel> html, string linkText, string actionName)
        {
            string s;
            //if (imageName != null)
            //{
                //s = string.Format(
                //        "<a class=\"app-link pull-right\" href=\"/{0}?Length=11\" style=\"padding-left: 15px; padding-right: 15px; \"><i class=\"{1}\"></i> {2}</a>",
                //        actionName,
                //        imageName,
                //        linkText);
            //}
            //else
            //{
                s = string.Format(
                        "<a class=\"app-link pull-right\" href=\"/{0}?Length=11\" style=\"padding-left: 15px; padding-right: 15px; \">{1}</a>",
                        actionName,
                        linkText);
            //}
            return MvcHtmlString.Create(s);
        }

        //public static MvcHtmlString GetBackLink(this HtmlHelper html)
        //{
        //    const string defaultAction = "Index";

        //    string returnUrl = HttpContext.Current.Request["returnUrl"];

        //    // If there's return url param redirect to there
        //    if (!String.IsNullOrEmpty(returnUrl))
        //    {
        //        return new MvcHtmlString("<a class=\"app-action-link\" href=" + returnUrl + " ><i class=\"fa fa-arrow-circle-left\"></i> Back</a>");
        //    }

        //    // if user just posted and there's no returnurl, redirect us to default
        //    if (HttpContext.Current.Request.HttpMethod == "POST")
        //    {
        //        return html.ActionLink("Back", defaultAction);
        //    }

        //    // we didn't post anything so we can safely go back to previous url
        //    return new MvcHtmlString("<a class=\"app-action-link\"href=" + HttpContext.Current.Request.UrlReferrer.ToString() + " ><i class=\"fa fa-arrow-circle-left\"></i> Back</a>");
        //}

        public static MvcHtmlString GetFixedBackLink(this HtmlHelper html, string action)
        {

            //var binding = html.Button(text, actionName, controllerName, null, new { @class = "app-image-link" });

            ////            var binding = html.HyperlinkButton(text, actionName, "TimeProject", routeValues, new { @class = "app-td-image-link", @style = "padding-left: 15px; padding-right: 15px;" });
            //binding.InnerHtml = string.Format("<i class=\"{0}\" title=\"{1}\"></i>{2}", imageName, text, "");
            //return binding;

            return new MvcHtmlString("<a class=\"back-link\" href=" + action + "><i class=\"fa fa-arrow-left\"></i></a>");
        }

        public static KnockoutBinding<TModel> GetTextBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {            
            return html.TextBox(text, new { @type = "text" }).ValueUpdate(KnockoutValueUpdateKind.AfterKeyDown);
        }

        public static KnockoutBinding<TModel> GetTextBoxLong<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.TextBox(text, new { @type = "text", @class = "long-text" }).ValueUpdate(KnockoutValueUpdateKind.AfterKeyDown);
        }

        public static KnockoutBinding<TModel> GetTextArea<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.TextArea(text, new { @type = "text", @class = "long-textarea" }).ValueUpdate(KnockoutValueUpdateKind.AfterKeyDown);
        }

        public static KnockoutBinding<TModel> GetLoginUsernameTextBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.TextBox(text, new { @type = "text", @class = "login-text", @placeholder = "Username" }).ValueUpdate(KnockoutValueUpdateKind.AfterKeyDown);
        }

        public static KnockoutBinding<TModel> GetLoginPasswordTextBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            var binding = html.Password(text, new { @class = "login-text", @placeholder = "Password" });
            return binding;
        }

        public static KnockoutBinding<TModel> GetPasswordTextBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.Password(text);
        }

        public static KnockoutBinding<TModel> GetReadonlyTextBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text)
        {
            return html.TextBox(text, new { @type = "text", @readonly = "true" });
        }

        public static KnockoutBinding<TModel> TrBegin<TModel>(this KnockoutHtml<TModel> html, KnockoutContext<TModel> context, string actionName)
        {
            // TODO: Tässä context kaivettava html:stä -> on protected ominaisuus.

            return new MyKnockoutBinding<TModel>(context)
            {
                TagName = "tr",
                WriteStartTag = true,
                WriteEndTag = false,
                HtmlStyles = "cursor: pointer;"
            }.Click(actionName, controllerName, new { index = "$index()" });
        }

        public static KnockoutBinding<TModel> TrEnd<TModel>(this KnockoutHtml<TModel> html, KnockoutContext<TModel> context)
        {
            // TODO: Tässä context kaivettava html:stä -> on protected ominaisuus.

            return new MyKnockoutBinding<TModel>(context)
            {
                TagName = "tr",
                WriteStartTag = false,
                WriteEndTag = true
            };
        }

        public static KnockoutBinding<TModel> GetTd<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> text, KnockoutContext<TModel> context)
        {
            // TODO: Tässä context kaivettava html:stä -> on protected ominaisuus.

            return new MyParentKnockoutBinding<TModel>(context, html.Span(text)) {TagName = "td"};
        }

        public static KnockoutBinding<TModel> GetCheckBox<TModel>(this KnockoutHtml<TModel> html, Expression<Func<TModel, object>> @checked)
        {
            return html.CheckBox(@checked);
        }
       
        private sealed class MyParentKnockoutBinding<TModel> : KnockoutBinding<TModel>
        {
            private readonly KnockoutTagBuilder<TModel> _tagBuilder;

            public string TagName { get; set; }

            public MyParentKnockoutBinding(KnockoutContext<TModel> context, KnockoutTagBuilder<TModel> tagBuilder)
                : base(context, null, null)
            {
                _tagBuilder = tagBuilder;
            }

            public override string ToHtmlString()
            {
                string inner = _tagBuilder.ToHtmlString();

                string s = string.Format("<{0}>{1}</{2}>", TagName, inner, TagName);
                return s;
            }
        }

        private sealed class MyKnockoutBinding<TModel> : KnockoutBinding<TModel>
        {
            public MyKnockoutBinding(KnockoutContext<TModel> context)
                : base(context, null, null)
            {
            }

            public string TagName { get; set; }
            public bool WriteStartTag { get; set; }
            public bool WriteEndTag { get; set; }
            public string HtmlStyles { get; set; }

            public override string ToHtmlString()
            {
                StringBuilder s = new StringBuilder();

                if (WriteStartTag)
                {
                    StringBuilder middle = new StringBuilder();

                    if (string.IsNullOrEmpty(HtmlStyles) == false)
                    {
                        middle.Append("style=\"");
                        middle.Append(HtmlStyles);
                        middle.Append("\" ");
                    }

                    string data = base.ToHtmlString();
                    middle.Append(data);

                    s.Append(string.Format("<{0} {1}>", TagName, middle.ToString()));
                }

                if (WriteEndTag)
                {
                    s.Append(string.Format("</{0}>", TagName));
                }

                return s.ToString();
            }
        }

        public static HtmlString Init<TModel>(this KnockoutContext<TModel> context, TModel model, string wrapperId = "", bool applyOnDocumentReady = false)
        {
            HtmlString html = context.Apply(model, wrapperId, applyOnDocumentReady);

            string s = html.ToHtmlString();

            int injectionPoint = s.LastIndexOf("ko.applyBindings(viewModel);", StringComparison.InvariantCulture);

            s = s.Insert(injectionPoint,
                         "ko.watch(viewModel, { depth: -1 }, function(parents, child) { hitChangeTracker(viewModel, child()); });" + Environment.NewLine);


            html = new HtmlString(s);
            return html;
        }


    }
}